IntentMemoryStorage
IntentMemoryStorage is an in-memory storage mechanism used inside AppIntent execution environments.
It allows multiple AppIntents—such as multi-step workflows involving SnippetIntents—to share temporary data.
However, its lifecycle does not follow the lifecycle of AppIntent execution or Script.exit().
It is controlled by the system’s management of the Extension process, which is unpredictable.
This document describes its real behavior, its storage scopes, how the system manages JSContexts in Shortcuts, Widgets, and Live Activities, and how developers should use it safely.
Overview
Each AppIntent in Scripting runs inside its own Script Execution Context (JSContext).
When:
- The AppIntent’s
perform() finishes, or
Script.exit() is called
the execution ends.
But this does not mean that IntentMemoryStorage (or the JSContext) is destroyed.
Instead:
IntentMemoryStorage persists as long as the system keeps the Extension process alive
It will only be cleared when:
- The Extension process is terminated by the system
- The system decides to reclaim memory
- The environment hosting the Intent Extension or Widget Extension is destroyed
Therefore:
- Running the same Shortcut again may read leftover values from the previous run.
- Widget or Live Activity AppIntent calls may reuse the same JSContext, preserving MemoryStorage.
- MemoryStorage can disappear at any time if the system kills the Extension process.
This behavior is normal and inherent to the AppExtension lifecycle.
IntentMemoryStorage is:
A short-lived, extension-scoped, non-persistent memory store
Storage Scopes
IntentMemoryStorage provides two scopes.
1. Script-scoped (default)
When shared: true is not provided:
- Storage belongs to a single script project
- Only AppIntents from the same script can access it
- System may keep it alive across executions
- It is cleared when the system finally terminates the Extension process
Useful for multi-step flows inside a single script.
2. Shared storage
When { shared: true } is specified:
- All scripts can access the same shared memory space
- Useful for cross-script workflow coordination
- Still relies on the same Extension process lifecycle
- Data disappears when the Extension is killed
Both scopes are temporary and tied to the system’s handling of the Extension environment.
System Lifecycle and JSContext Behavior
IntentMemoryStorage’s behavior is entirely dependent on how the OS manages the AppIntent/Widget Extension process.
Below is a complete explanation of observed behaviors.
Case 1: Shortcuts running an Intent
When a Shortcut executes an Intent:
- The AppIntent finishes
- The Script.exit() returns a result
- The JSContext used for execution is destroyed
But:
IntentMemoryStorage does not necessarily clear.
If the system keeps the Intent Extension process alive, the stored data remains in memory.
Therefore:
Running the same Shortcut again may still read values saved previously.
This is expected behavior.
Case 2: Widgets calling AppIntents
Widget Extensions behave differently:
- The system prefers reusing the same JSContext
- Therefore, IntentMemoryStorage may persist across multiple AppIntent calls
- But the system may kill the Widget Extension at any time
- When this happens, both the JSContext and MemoryStorage are cleared
Hence:
MemoryStorage may survive across widget updates, or it may vanish unpredictably.
Case 3: Live Activity calling AppIntents
Live Activity environments also reuse JSContexts:
- Multiple AppIntent calls often share the same JSContext
- MemoryStorage persists as long as the Extension stays alive
- The system may terminate the Live Activity extension at any time
- MemoryStorage then disappears immediately
Final Lifecycle Summary
| Event |
Does MemoryStorage clear immediately? |
| AppIntent finish |
No |
| Script.exit() |
No |
| Shortcut flow finishes |
Not necessarily |
| Widget AppIntent call |
Not necessarily |
| Live Activity AppIntent call |
Not necessarily |
| System kills the Extension process |
Yes (completely cleared) |
Therefore:
MemoryStorage should never be treated as reliable or persistent.
It may remain, or it may disappear at any time.
API Definition
1namespace IntentMemoryStorage {
2 function get<T>(key: string, options?: { shared?: boolean }): T | null
3 function set(key: string, value: any, options?: { shared?: boolean }): void
4 function remove(key: string, options?: { shared?: boolean }): void
5 function contains(key: string, options?: { shared?: boolean }): boolean
6 function clear(): void
7 function keys(): string[]
8}
Notes:
shared applies only to get / set / remove / contains
clear() and keys() operate only on script-scoped storage, never on shared storage
API Details
get
1function get<T>(key: string, options?: { shared?: boolean }): T | null
Retrieves a value.
However:
- If the Extension is still alive → may return leftover values
- If the Extension was killed → returns null
Examples:
Script-scoped:
1const color = IntentMemoryStorage.get<string>("color")
Shared:
1const token = IntentMemoryStorage.get<string>("token", { shared: true })
set
1function set(key: string, value: any, options?: { shared?: boolean }): void
Stores a value in the selected scope.
remove
1function remove(key: string, options?: { shared?: boolean }): void
Deletes the key in the selected scope.
contains
1function contains(key: string, options?: { shared?: boolean }): boolean
Checks whether a key exists.
This depends on whether the Extension process has remained alive.
clear
Clears script-scoped memory only.
To clear shared memory, remove keys manually.
keys
1function keys(): string[]
Returns keys in script-scoped storage.
Shared keys must be tracked manually by the developer.
Usage Scenarios
Script-scoped (default)
Good for:
- Multi-step flows inside a single script
- SnippetIntent → AppIntent → SnippetIntent
- Temporary UI state
- Step numbers, temporary selections
Shared storage
Good for:
- Multi-script cooperation
- Coordinating global workflow IDs
- Sharing ephemeral state across multiple AppIntent calls
Not Recommended For
- Persistent data
- Large objects (images, binary, long text)
- Data that must be reliably present
- Data that must be reliably cleared
- Any workflow requiring deterministic behavior
Use instead:
Storage for durable key–value data
FileManager for files in the shared App Group directory
Examples
Script-scoped
1IntentMemoryStorage.set("color", "red")
2
3const color = IntentMemoryStorage.get<string>("color")
Shared across scripts
Script A:
1IntentMemoryStorage.set("sessionID", "12345", { shared: true })
Script B:
1const id = IntentMemoryStorage.get<string>("sessionID", { shared: true })
Storage Structure Example
If you store:
1IntentMemoryStorage.set("color", "green")
2IntentMemoryStorage.set("step", 2)
3IntentMemoryStorage.set("token", "xyz", { shared: true })
Then the extension process holds:
Script-scoped:
1{
2 "color": "green",
3 "step": 2
4}
Shared:
Both disappear once the system kills the Extension process.
Best Practices
-
Treat MemoryStorage as an in-memory cache, not a storage layer
-
Never assume the value will exist
-
Never assume the value will be cleared
-
Do not store large data
-
Use structured keys like:
"workflow.step"
"ui.selectedColor"
"global.sessionID"
-
For persistent or critical data, always use Storage or FileManager